home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Libraries / VideoToolbox 97.08.16 / VideoToolboxSources / GDTime.c < prev    next >
Encoding:
Text File  |  1997-04-10  |  17.1 KB  |  441 lines  |  [TEXT/CWIE]

  1. /*
  2. GDTime.c
  3.  
  4. All these routines measure the timing of some aspect of the function of a video
  5. device. For background, read “Video synch” and run TimeVideo, which reports the
  6. results of running all these routines.
  7.  
  8. double GDFrameRate(GDHandle device) measures the frame rate of a video device in
  9. Hz. (A NULL “device” argument to this routine, or any of the routines below,
  10. requests use of the System VBL interrupt, which normally runs at 60.15 Hz.) It
  11. times by counting VBL interrupts (discarding any spurious ones, to deal with the
  12. problem described in the next paragraph).
  13.  
  14. double GDVBLRate(GDHandle device) measures the rate of VBL interrupts generated
  15. by a video device in Hz. According to Apple’s Designing Cards and Drivers book
  16. and other documentation, the video driver and card are supposed to generate one
  17. VBL interrupt per frame. However, many don’t. E.g. Apple’s 4•8 and 8•24 video
  18. cards issue several interrupts per frame. Read the “Video synch” file.
  19.  
  20. double GDMovieSize(GDHandle device,int quickly) measures what fraction of the
  21. screen you can fill with a real-time movie (a new image on each frame), using
  22. CopyBitsQuickly (if quickly!=0) or CopyBits (if quickly==0) to copy from memory
  23. to video card. At one time CopyBitsQuickly() was much faster than CopyBits(),
  24. but my latest measurements, using GDMovieRate, indicate that there is no longer
  25. any difference in speed. However, CopyBitsQuickly ignores the color tables and
  26. CopyBits uses them. Of course, when you’re showing movies you don’t want to
  27. waste time with color tables, so GDMovieRate() makes a PixMap that shares the
  28. device’s color table. For reasons that I don’t understand even in that case
  29. using CopyBits to copy from, and then back to, the screen doesn’t always preserve
  30. the original colors.
  31.  
  32. double GDMovieRate(GDHandle device,int quickly) measures the rate (images/s) at
  33. which you can show a full-screen movie.
  34.  
  35. error=GDTimeClut(device,GDSetEntries,clutEntries,&s,&frames,&missingFrames,&frameRate);
  36. measures how long it takes to load the clut. It measures in two kinds of units
  37. simultaneously, frames and seconds. You supply the function to be tested, e.g.
  38. SetEntriesQuickly or GDSetEntries. (If gdType==directType GDTimeClut will
  39. automatically substitute GDDirectSetEntries for GDSetEntries.) The second
  40. argument, “clutEntries”, specifies how many clut entries you want to update each
  41. time, or zero for all. GDTimeClut also measures the frame rate independently,
  42. which it returns, after using it to estimate how many frame interrupts were
  43. missed during each clut load. If there’s at least one frame missing or if the
  44. frame count is very small, less than 0.5 per call, then it estimates the frames
  45. directly from the time. You may substitute NULL for any of the pointer-to-double
  46. arguments.
  47.  
  48. NOTES:
  49. It is of interest to time GDSetEntries (and its sibling GDDirectSetEntries) at
  50. both normal (zero) and high (7) processor interrupt priority, because some
  51. drivers are asynchronous when run at low priority, returning immediately and
  52. deferring the actual clut loading until the vbl interrupt occurs, but Apple
  53. specifies that all drivers must be synchronous when run at high priority. You do
  54. this by supplying the new routine GDSetEntriesByTypeHighPriority as an argument
  55. to GDTimeClut.
  56.  
  57. Similarly, while the problem of multiple interrupts per frame is dealt with
  58. satisfactorily by VBLInstall.c, using the scheme suggested by Raynald Comtois,
  59. it is of technical interest to follow up the report that there are no extra
  60. interrupts if the processor instruction cache is disabled. One theory to account
  61. for this is that perhaps disabling the cache causes the interrupt service
  62. routine to take long enough that the hardware interrupt pulse has terminated
  63. before the routine exits. Thus I would like to write a routine called
  64. GDSetEntriesNoCache, but I don’t know how to disable the cache.
  65.  
  66. HISTORY:
  67. 8/22/92 dgp    wrote ‘em.
  68. 8/26/92    dgp    added clutEntries argument
  69. 8/28/92    dgp    updated to use new reentrant Timer.c
  70. 9/11/92    dgp enhanced GDFrameRate() to discard bogus VBL interrupts and to return
  71.             true frame rate, as suggested by Raynald Comtois. Added GDVBLRate(), 
  72.             which corresponds to the old GDFrameRate().
  73. 9/15/92    dgp    GDMovieRate() now asks the video driver what mode we’re in, just in case
  74.             QuickDraw’s been fooled.
  75. 9/17/92    dgp    Added second argument to GDMovieRate() to select CopyBitsQuickly vs CopyBits.
  76. 10/5/92    dgp    fixed bug in GDMovieRate() that caused crashes or noop when testing other 
  77.             than the main device.
  78. 10/9/92    dgp    now actually initialize the linearTable for direct clut.
  79. 10/10/92 dgp Squeezed out extra space from rowBytes in GDMovieRate() so as not to show
  80.             garbage. Now use Temporary memory if there isn’t enough memory in the application 
  81.             heap to show a full-screen movie. Show movie for 3 seconds. 
  82. 10/13/92 dgp In response to a report from Tom Busey, that frames were going uncounted
  83.             during the clut timing, which seems to be a problem with some video drivers,
  84.             GDFramesPerClutUpdate() now double checks the timing in secs, and if
  85.             it finds a discrepancy, prints a warning to the screen and reports a
  86.             corrected values based on the timing in secs. This should be more reliable.
  87. 10/13/92 dgp Fixed error in printf in GDFrameRate().
  88. 11/23/92 dgp Set nominalBits equal to pixelSize instead of Log2L(ctsize).
  89. 12/30/92 dgp Commented out warning from GDFramesPerClutUpdate().
  90.              •Check for cscSetEntries error in the clut timing routines, and return
  91.              NAN in that case. •Use trial and error to determine clut size.
  92. 1/4/92    dgp    GDTimeClutUpdate now returns NAN on GDSetEntries error.
  93. 1/6/92    dgp    Fixed computation of linearTable, so CLUT is preserved by GDTimeClutUpdate.
  94. 1/11/93    dgp    Enhanced GDMovieRate() to work even when Color QuickDraw is absent, by 
  95.             calling the new routine GDMovieRateNoColorQuickDraw().
  96.             Check returned Ptr from NewTimer() to make sure it’s ok; will be NULL
  97.             if computer only has Standard Time Manager.
  98. 1/24/93    dgp    Reduced timing interval from 3 to 1 s for movies.
  99. 3/11/93    dgp created GDTimeClut, based on GDTimeClutUpdate and
  100.             GDFramesPerClutUpdate. The enhancements are 1. you
  101.             supply the function to be tested, e.g. SetEntriesQuickly
  102.             or GDSetEntries. 2. It doesn’t print or exit, always
  103.             returning with an informative OSErr. 3. It measures frame
  104.             rate independently, which it returns, and also uses it to
  105.             estimate how many frame interrupts were missed during
  106.             each clut load. 4. If there’s at least one frame missing
  107.             or if the frame count is very small, less than 0.5 per
  108.             call, then it estimates the frames directly from the
  109.             time.
  110. 3/15/93    dgp    Fixed portRect clipping error in GDMovieRateNoColorQuickDraw.
  111. 4/17/93    dgp    Merged GDFrameRate.c and GDTimeClut.c to produce GDTime.c
  112. 4/19/93    dgp    Fixed bug in GDTimeClut that used garbage color table in place of
  113.             linear color table. Now uses GDNewLinearColorTable.
  114. 4/25/93    dgp    Changed struct from static to automatic.
  115. 5/31/94    dgp    Made compatible with Apple's Universal Headers. Replaced MBarHeight by
  116.             GetMBarHeight() and added calls to NewVBLProc and DisposeRoutineDescriptor.
  117. 9/5/94 dgp removed assumption in printf's that int==short.
  118. 2/25/97    dgp    updated to use Seconds.c instead of Timer.c
  119. 3/19/97    dgp    updated comments above to refer to Microseconds instead of Timer.c
  120. 4/10/97    dgp    Eliminate stuff that was conditional on !UNIVERSAL_HEADERS.
  121. */
  122. #include "VideoToolbox.h"
  123. #include <LowMem.h>        // LMGetMBarHeight,LMSetMBarHeight
  124. double GDMovieRateNoColorQuickDraw(int quickly);
  125. // Original typedef is in VideoToolbox.h
  126. //typedef OSErr (*SetEntriesFunction)(GDHandle device,short start,short count
  127. //    ,ColorSpec *aTable);
  128. #define CALLS 30            // fewer for speed, more for accuracy
  129. #define FRAMES 10            // fewer for speed, more for accuracy
  130. #define SHOW_MOVIE_WINDOW 0    // A matter of taste, but I prefer not to show it.
  131. #define SECONDS 0.5            // Movie duration.
  132.  
  133. OSErr GDTimeClut(GDHandle device,SetEntriesFunction function,short clutEntries
  134.     ,double *sPtr,double *framesPtr,double *missingFramesPtr,double *frameRatePtr)
  135. {
  136.     int error;
  137.     short clutSize,i;
  138.     ColorSpec *table,*linearTable=NULL;
  139.     VBLTaskAndA5 vblData;
  140.     long frames;
  141.     double s,sStart,missingFrames,frameRate;
  142.  
  143.     if(sPtr!=NULL)*sPtr=NAN;
  144.     if(framesPtr!=NULL)*framesPtr=NAN;
  145.     if(missingFramesPtr!=NULL)*missingFramesPtr=NAN;
  146.     if(frameRatePtr!=NULL)*frameRatePtr=NAN;
  147.     if(device==NULL || (*device)->gdType==fixedType){
  148.         if(frameRatePtr!=NULL)*frameRatePtr=GDFrameRate(device);
  149.         return 0;
  150.     }
  151.     clutSize=GDClutSize(device);
  152.     if(clutEntries<0 || clutEntries>clutSize)return 1;
  153.     if(clutEntries==0)clutEntries=clutSize;
  154.     vblData.subroutine=NULL;                        // setup frame counter
  155.     error=VBLInstall(&vblData,device,CALLS*20);        // setup frame counter
  156.     if(error)return error;
  157.     if((*device)->gdType==directType){
  158.         if(function==GDSetEntries)function=GDDirectSetEntries;
  159.         table=linearTable=GDNewLinearColorTable(device);
  160.         if(linearTable==NULL)return MemError();
  161.     }else table=((**(**(**device).gdPMap).pmTable)).ctTable;
  162.     vblData.vbl.vblCount=1;                            // Enable interrupt service routine
  163.     for(i=-1;i<CALLS;i++) {
  164.         error=(function)(device,0,clutEntries-1,table);
  165.         if(i==-1){
  166.             sStart=Seconds();
  167.             frames=vblData.framesLeft;
  168.         }
  169.         if(error)break;
  170.     }
  171.     frames-=vblData.framesLeft;
  172.     s=Seconds()-sStart;
  173.     VBLRemove(&vblData);
  174.     if(linearTable!=NULL)DisposePtr((Ptr)linearTable);
  175.     if(error)return error;
  176.     
  177.     // Estimate number of missing frames by discrepancy between frames and secs.
  178.     frameRate=GDFrameRate(device);
  179.     missingFrames=s*frameRate-frames;
  180.     
  181.     // Return results
  182.     if(sPtr!=NULL)*sPtr=s/CALLS;
  183.     if(framesPtr!=NULL){
  184.         if(fabs(missingFrames)>1. || frames<CALLS/2) *framesPtr=frameRate*s/CALLS;
  185.         else *framesPtr=frames/(double)CALLS;
  186.     }
  187.     if(missingFramesPtr!=NULL)*missingFramesPtr=missingFrames/CALLS;
  188.     if(frameRatePtr!=NULL)*frameRatePtr=frameRate;
  189.     return 0;
  190. }
  191.  
  192. double GDFrameRate(GDHandle device)
  193. {
  194.     VBLTaskAndA5 vblData;
  195.     register long frames;
  196.     int error;
  197.     double s,sStart;
  198.     
  199.     vblData.subroutine=NULL;
  200.     error=VBLInstall(&vblData,device,FRAMES);
  201.     if(error)PrintfExit("GDFrameRate: VBLInstall: error %d\n",error);
  202.     vblData.vbl.vblCount=1;                // Enable interrupt service routine
  203.     frames=vblData.framesDesired-2;
  204.     while(vblData.framesLeft>frames) ;    // wait for second frame
  205.     sStart=Seconds();
  206.     while(vblData.framesLeft) ;            // wait for last frame
  207.     s=Seconds()-sStart;
  208.     VBLRemove(&vblData);
  209.     return frames/s;
  210. }
  211.  
  212. #if !(MATLAB && THINK_C)    // THINK C's linker doesn't strip unused code.
  213.  
  214. double GDVBLRate(GDHandle device)
  215. {
  216.     VBLTaskAndA5 vblData;
  217.     register long frames;
  218.     int error;
  219.     double s,sStart;
  220.     
  221.     vblData.subroutine=(void *)NewVBLProc(SimpleVBLSubroutine);
  222.     error=VBLInstall(&vblData,device,FRAMES);
  223.     if(error)PrintfExit("GDVBLRate: VBLInstall: error %d\n",error);
  224.     vblData.vbl.vblCount=1;                // Enable interrupt service routine
  225.     frames=vblData.framesDesired-2;
  226.     while(vblData.framesLeft>frames) ;    // wait for second frame
  227.     sStart=Seconds();
  228.     while(vblData.framesLeft) ;            // wait for last frame
  229.     s=Seconds()-sStart;
  230.     VBLRemove(&vblData);
  231.     DisposeRoutineDescriptor(vblData.subroutine);
  232.     return frames/s;
  233. }
  234.  
  235. double GDMovieSize(GDHandle device,int quickly)
  236. {
  237.     return GDMovieRate(device,quickly)/GDFrameRate(device);
  238. }
  239.  
  240. double GDMovieRate(GDHandle device,int quickly)
  241. {
  242.     register long image;
  243.     long images;
  244.     int error;
  245.     OSErr osErr;
  246.     double s=NAN,sStart,fractionOfFrame=NAN;
  247.     PixMap **pm;
  248.     unsigned long bytes;
  249.     GDHandle oldDevice;
  250.     WindowPtr window,oldPort;
  251.     Rect r,rLocal;
  252.     Ptr oldBaseAddr;
  253.     Handle saveSpace,bufferHandle;
  254.     long osAttr;
  255.     int tempMem;
  256.     
  257.     if(!QD8Exists())return GDMovieRateNoColorQuickDraw(quickly);
  258.     oldDevice=GetGDevice();
  259.     SetGDevice(device);
  260.     pm=NewPixMap();
  261.     SetGDevice(oldDevice);
  262.     if(pm==NULL)goto done0;
  263.     HLock((Handle)pm);
  264.     // The color table is needed for CopyBits(); CopyBitsQuickly doesn't care.
  265.     (**pm).pmTable=(**(**device).gdPMap).pmTable;    // share device's color table
  266.     if(device==GetMainDevice())(**pm).bounds.top+=LMGetMBarHeight();
  267.     if(SHOW_MOVIE_WINDOW){
  268.         (**pm).bounds.top+=19;    // Allow room for window title
  269.         InsetRect(&(**pm).bounds,32,32);
  270.     }
  271.     bufferHandle=NULL;
  272.     bytes=(**pm).bounds.right-(**pm).bounds.left;
  273.     bytes*=(**pm).pixelSize;
  274.     bytes=((bytes+31)/32)*4;    // convert bits to bytes, rounding up to multiple of 4
  275.     (**pm).rowBytes &= ~0x3fff;
  276.     (**pm).rowBytes |= bytes;
  277.     Gestalt(gestaltOSAttr,&osAttr);
  278.     tempMem=osAttr & 1L<<gestaltTempMemSupport;
  279.     while(1){
  280.         bytes=(**pm).rowBytes & 0x3fff;
  281.         bytes*=(**pm).bounds.bottom-(**pm).bounds.top;
  282.         if(bytes==0)goto done1;
  283.         saveSpace=NewHandle(2000);    // save some space
  284.         bufferHandle=NewHandle(bytes+1200);    // extra is for drifting
  285.         if(saveSpace!=NULL)DisposeHandle(saveSpace);
  286.         if(bufferHandle==NULL && tempMem)bufferHandle=TempNewHandle(bytes+1200,&osErr);
  287.         error=osErr;
  288.         if(bufferHandle!=NULL)break;
  289.         // Halve the window's height before trying again
  290.         (**pm).bounds.bottom=(**pm).bounds.top+((**pm).bounds.bottom-(**pm).bounds.top)/2;
  291.     }
  292.     HLock(bufferHandle);
  293.     (**pm).baseAddr=*bufferHandle;
  294.     GetPort(&oldPort);
  295.     r=(**pm).bounds;
  296.     window=NewCWindow(NULL,&r,"\pmovie",0,0,(WindowPtr)-1,0,0);    // don't show it yet
  297.     if(window==NULL)goto done2;
  298.     SetPort(window);
  299.     HLock((Handle)((CWindowPtr)window)->portPixMap);
  300.     SetGDevice(device);
  301.     rLocal=r;
  302.     GlobalToLocalRect(&rLocal);
  303.     sStart=Seconds();
  304.     if(quickly)CopyBitsQuickly((BitMap *)*((CWindowPtr)window)->portPixMap,(BitMap *)*pm
  305.         ,&rLocal,&r,srcCopy,NULL);    // copy screen to memory
  306.     else{
  307.         CopyBits((BitMap *)*((CWindowPtr)window)->portPixMap,(BitMap *)*pm
  308.         ,&rLocal,&r,srcCopy,NULL);    // copy screen to memory
  309.         error=QDError();
  310.         if(error){
  311.             printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
  312.             goto done3;
  313.         }
  314.     }
  315.     s=Seconds()-sStart;    // rough estimate of time for one image
  316.     if(SHOW_MOVIE_WINDOW || !quickly)ShowWindow(window);// CopyBits won't copy to a hidden window
  317.     sStart=Seconds();
  318.     oldBaseAddr=(**pm).baseAddr;
  319.     images=ceil(SECONDS/s);
  320.     for(image=images;image>0;image--){
  321.         if(image==1)(**pm).baseAddr=oldBaseAddr;
  322.         else (**pm).baseAddr+=4;    // Drift image to prove it's a movie
  323.         // We drift by multiples of 4 bytes because long-aligned copying is faster.
  324.         if(quickly)CopyBitsQuickly((BitMap *)*pm,(BitMap *)*((CWindowPtr)window)->portPixMap
  325.             ,&r,&rLocal,srcCopy,NULL);    // copy memory to screen
  326.         else CopyBits((BitMap *)*pm,(BitMap *)*((CWindowPtr)window)->portPixMap
  327.             ,&r,&rLocal,srcCopy,NULL);    // copy memory to screen
  328.     }
  329.     s=Seconds()-sStart;
  330.     fractionOfFrame=(long)(r.bottom-r.top)*(long)(r.right-r.left);
  331.     r=(**(**device).gdPMap).bounds;
  332.     fractionOfFrame/=(long)(r.bottom-r.top)*(long)(r.right-r.left);
  333. done3:
  334.     SetPort(oldPort);
  335.     DisposeWindow(window);
  336. done2:
  337.     DisposHandle(bufferHandle);
  338. done1:
  339.     (**pm).pmTable=NULL;
  340.     DisposePixMap(pm);
  341. done0:
  342.     SetGDevice(oldDevice);
  343.     return images*fractionOfFrame/s;
  344. }
  345.  
  346. double GDMovieRateNoColorQuickDraw(int quickly)
  347. {
  348.     register long image;
  349.     long images;
  350.     int error=0;
  351.     OSErr osErr;
  352.     double s=NAN,sStart,fractionOfFrame=NAN;
  353.     BitMap bitmap;
  354.     unsigned long bytes;
  355.     Rect r;
  356.     Ptr oldBaseAddr;
  357.     Handle saveSpace,bufferHandle=NULL;
  358.     long osAttr;
  359.     int tempMem;
  360.     GrafPort portRec,*port=&portRec,*oldPort;
  361.     
  362.     GetPort(&oldPort);
  363.     OpenPort(port);
  364.     SetPort(port);
  365.     bitmap=port->portBits;
  366.     bytes=bitmap.bounds.right-bitmap.bounds.left;
  367.     bitmap.rowBytes=((bytes+31)/32)*4;    // convert bits to bytes, round up to mult of 4
  368.     Gestalt(gestaltOSAttr,&osAttr);
  369.     tempMem=osAttr & 1L<<gestaltTempMemSupport;
  370.     while(1){
  371.         bytes=bitmap.rowBytes & 0x3fff;
  372.         bytes*=bitmap.bounds.bottom-bitmap.bounds.top;
  373.         if(bytes==0)goto done;
  374.         saveSpace=NewHandle(2000);    // save some space
  375.         bufferHandle=NewHandle(bytes+600);    // extra is for drifting
  376.         if(saveSpace!=NULL)DisposeHandle(saveSpace);
  377.         if(bufferHandle==NULL && tempMem)bufferHandle=TempNewHandle(bytes+600,&osErr);
  378.         error=osErr;
  379.         if(bufferHandle!=NULL)break;
  380.         // Halve the window's height before trying again
  381.         bitmap.bounds.bottom=bitmap.bounds.top+(bitmap.bounds.bottom-bitmap.bounds.top)/2;
  382.     }
  383.     HLock(bufferHandle);
  384.     bitmap.baseAddr=*bufferHandle;
  385.     r=bitmap.bounds;
  386.     sStart=Seconds();
  387.     if(quickly)CopyBitsQuickly(&port->portBits,&bitmap
  388.         ,&r,&r,srcCopy,NULL);    // copy screen to memory
  389.     else CopyBits(&port->portBits,&bitmap
  390.         ,&r,&r,srcCopy,NULL);    // copy screen to memory
  391.     s=Seconds()-sStart;            // approximate time for one image
  392.     error=QDError();
  393.     if(!quickly && error){
  394.         printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
  395.         goto done;
  396.     }
  397.     oldBaseAddr=bitmap.baseAddr;
  398.     images=ceil(SECONDS/s);    // Let's time for this many seconds.
  399.     sStart=Seconds();
  400.     for(image=images;image>0;image--){
  401.         if(image==1)bitmap.baseAddr=oldBaseAddr;
  402.         else bitmap.baseAddr+=2;    // Drift the image, to prove it's a movie
  403.         if(quickly)CopyBitsQuickly(&bitmap,&port->portBits
  404.             ,&r,&r,srcCopy,NULL);    // copy memory to screen
  405.         else CopyBits(&bitmap,&port->portBits
  406.             ,&r,&r,srcCopy,NULL);    // copy memory to screen
  407.     }
  408.     s=Seconds()-sStart;
  409.     error=QDError();
  410.     if(!quickly && error){
  411.         printf("GDMovieRate: CopyBits generated QuickDraw error %d\n",error);
  412.         goto done;
  413.     }
  414.     fractionOfFrame=(long)(r.bottom-r.top)*(long)(r.right-r.left);
  415.     r=port->portBits.bounds;
  416.     fractionOfFrame/=(long)(r.bottom-r.top)*(long)(r.right-r.left);
  417. done:
  418.     SetPort(oldPort);
  419.     ClosePort(port);
  420.     if(bufferHandle==NULL){
  421.         printf("GDMovieRate: Not enough memory!\n");
  422.         return NAN;
  423.     }
  424.     DisposeHandle(bufferHandle);
  425.     return images*fractionOfFrame/s;
  426. }
  427.  
  428. double TickRate(void)
  429. {
  430.     double s,sStart;
  431.     long t;
  432.     
  433.     Delay(1,&t);
  434.     sStart=Seconds();
  435.     Delay(FRAMES,&t);
  436.     s=Seconds()-sStart;
  437.     return FRAMES/s;
  438. }
  439.  
  440. #endif // !(MATLAB && THINK_C)    // THINK C's linker doesn't strip unused code.
  441.